﻿using Microsoft.ExtendedReflection.ComponentModel;
using Microsoft.ExtendedReflection.Metadata;
using Microsoft.ExtendedReflection.Utilities.Safe.Diagnostics;
using MolesVS2012.Host;
using Microsoft.VisualStudio.TestTools.Common;
using Microsoft.VisualStudio.TestTools.Execution;
using Microsoft.VisualStudio.TestTools.TestAdapter;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;

namespace MolesVS2012.Host.Agent
{
    [Guid("AB32F7A1-E909-4e7c-93E5-1BCD5C5A85BB")]
    [Serializable]
    internal sealed class MolesAgentAdapter : MolesAdapterBase
    {
        public static readonly string HostType = typeof(MolesAgentAdapter).AssemblyQualifiedName;
        private readonly object runnerLock = new object();
        private string closeReason;
        private MolesAgentAdapter.State state;
        private Container container;
        private MolesAgentEngine engine;
        private MolesHostAdapterManager hostAdapterManager;

        static MolesAgentAdapter()
        {
        }

        public override void Initialize(IRunContext runContext)
        {
            SafeDebugger.BreakIf(MolesVsHostEnvironmentSettings.BreakOnAgentStart.Value);
            this.state = MolesAgentAdapter.State.Initializing;
            base.Initialize(runContext);
            this.container = new Container(new TypeEx[0]);
            this.engine = new MolesAgentEngine((IContainer)this.container, this, MolesVsHostOptions.FromEnvironment());
            this.GetService<RunResultSyncLogger>();
            this.hostAdapterManager = this.GetService<MolesHostAdapterManager>();
            this.state = MolesAgentAdapter.State.Initialized;
        }

        public static bool IsVersion2010()
        {
            return typeof(TestRunConfiguration).Assembly.GetName().Version.Major == 10;
        }

        public static bool IsVersion2012()
        {
            return typeof(TestRunConfiguration).Assembly.GetName().Version.Major == 11;
        }

        [Conditional("DEBUG")]
        private void AssertInitialized(string caller)
        {
        }

        public override void Run(ITestElement testElement, ITestContext testContext)
        {
            lock (this.runnerLock)
            {
                int local_0 = 0;
                ITestAdapter local_1;
                while (this.TryGetHostTestAdapter(testElement, testContext, out local_1))
                {
                    try
                    {
                        local_1.Run(testElement, testContext);
                        return;
                    }
                    catch (RemotingException exception_0)
                    {
                        if (local_0++ < 2)
                            this.Log.LogWarning("Missing Wiki Topic", "Agent", "remoting failure, retrying to execute {0}", new object[1]
              {
                (object) testElement.Name
              });
                        else
                            throw;
                    }
                    catch (Exception exception_1)
                    {
                        this.Log.LogErrorFromException(exception_1, "Missing Wiki Topic", "Agent", "an unexpected exception was thrown from the host ({0} - {1})", new object[2]
            {
              (object) exception_1.GetType(),
              (object) exception_1.Message
            });
                        return;
                    }
                }
                this.Log.LogError("Missing Wiki Topic", "Agent", "failed to start Pex Host for {0} (in {1})", new object[2]
        {
          (object) testElement.Name,
          (object) testElement.Storage
        });
            }
        }

        private bool TryGetHostTestAdapter(ITestElement testElement, ITestContext testContext, out ITestAdapter hostAdapter)
        {
            string str1 = testElement.Storage;
            if (this.TestRunConfiguration.IsDeploymentEnabled)
            {
                string str2 = Path.Combine(this.TestRunConfiguration.RunDeploymentOutDirectory, Path.GetFileName(str1));
                this.Log.LogVerbose("agent", "assembly deployed {0} -> {1}", (object)str1, (object)str2);
                str1 = str2;
            }
            if (!File.Exists(str1))
            {
                this.Log.LogError("Missing Wiki Topic", "Agent", "could not find {0}", new object[1]
        {
          (object) str1
        });
                hostAdapter = (ITestAdapter)null;
                return false;
            }
            else
            {
                string assembly = this.AnalyseAssemblyLocation(str1);
                if (assembly != null)
                    return this.hostAdapterManager.TryGetOrCreateHostAdapter(assembly, out hostAdapter);
                hostAdapter = (ITestAdapter)null;
                return false;
            }
        }

        private string AnalyseAssemblyLocation(string assemblyLocation)
        {
            return assemblyLocation;
        }

        public override void AbortTestRun()
        {
            Trace.TraceInformation("Pex: AbortTestRun");
            this.hostAdapterManager.ForAllHostAdapters((Action<ITestAdapter>)(host => host.AbortTestRun()));
        }

        public override void PauseTestRun()
        {
            Trace.TraceInformation("Pex: PauseTestRun");
            this.hostAdapterManager.ForAllHostAdapters((Action<ITestAdapter>)(host => host.PauseTestRun()));
        }

        public override void ResumeTestRun()
        {
            Trace.TraceInformation("Pex: ResumeTestRun");
            this.hostAdapterManager.ForAllHostAdapters((Action<ITestAdapter>)(host => host.ResumeTestRun()));
        }

        public override void StopTestRun()
        {
            Trace.TraceInformation("Pex: StopTestRun");
            this.hostAdapterManager.ForAllHostAdapters((Action<ITestAdapter>)(host => host.StopTestRun()));
            this.Close("Stop");
        }

        public override void Cleanup()
        {
            Trace.TraceInformation("Pex: CleanUp");
            if (this.state != MolesAgentAdapter.State.Initialized)
                return;
            if (this.hostAdapterManager != null)
                this.hostAdapterManager.CleanUpHostAdapters();
            this.Close("Cleanup");
        }

        public override void ReceiveMessage(object obj)
        {
            this.hostAdapterManager.ForAllHostAdapters((Action<ITestAdapter>)(host => host.ReceiveMessage(obj)));
        }

        public override void PreTestRunFinished(IRunContext runContext)
        {
            this.hostAdapterManager.ForAllHostAdapters((Action<ITestAdapter>)(host => host.PreTestRunFinished(runContext)));
        }

        public override void Dispose()
        {
            base.Dispose();
            if (this.state != MolesAgentAdapter.State.Initialized)
                return;
            this.Close("Dispose");
        }

        private void Close(string reason)
        {
            this.closeReason = reason;
            this.state = MolesAgentAdapter.State.Closing;
            MolesHostAdapterManager hostAdapterManager = this.hostAdapterManager;
            if (hostAdapterManager != null)
            {
                hostAdapterManager.CloseHostAdapters();
                this.hostAdapterManager = (MolesHostAdapterManager)null;
            }
            MolesAgentEngine molesAgentEngine = this.engine;
            if (molesAgentEngine != null)
            {
                this.engine = (MolesAgentEngine)null;
                molesAgentEngine.Dispose();
            }
            Container container = this.container;
            if (container != null)
            {
                this.container = (Container)null;
                container.Dispose();
            }
            this.state = MolesAgentAdapter.State.Closed;
        }

        private enum State
        {
            Uninitialized,
            Initializing,
            Initialized,
            Closing,
            Closed,
        }
    }
}
